#pragma once
#include "cem/diagnostics.h"

#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>

namespace cem
{

struct ConfigValue; // 前置声明，便于递归结构的定义

// 数组类型：由若干 ConfigValue 组成（指针存储，允许前置声明）
struct ConfigArray
{
    std::vector<std::shared_ptr<ConfigValue>> items;
};

// 对象类型：键值映射，值为 ConfigValue（指针存储，允许前置声明）
struct ConfigObject
{
    std::unordered_map<std::string, std::shared_ptr<ConfigValue>> props;
};

// 值类型：支持常见标量与容器（数组、对象）
using ConfigVariant = std::variant<std::nullptr_t,
                                   bool,
                                   int64_t,
                                   double,
                                   std::string,
                                   std::shared_ptr<ConfigArray>,
                                   std::shared_ptr<ConfigObject>>;

struct ConfigValue
{
    ConfigVariant value{};
};

// 只读配置目录树节点（不可变）
class ConfigDirectory
{
public:
    using Ptr = std::shared_ptr<const ConfigDirectory>;

    // 构造一个空目录
    ConfigDirectory() = default;

    // 使用名称与对象构造根或子目录
    ConfigDirectory(std::string name, ConfigObject object, std::vector<std::string> path = {})
        : _name(std::move(name)), _object(std::move(object)), _path(std::move(path))
    {
    }

    // 目录名称
    const std::string & name() const { return _name; }

    // 从根到当前节点的路径
    const std::vector<std::string> & path() const { return _path; }

    // 判断是否存在某个键
    bool has_key(const std::string & key) const { return _object.props.find(key) != _object.props.end(); }

    // 取子目录（对象类型），失败返回诊断
    std::optional<ConfigDirectory> subdir(const std::string & key, DiagnosticList * diags = nullptr) const;

    // 取标量类值（带类型检查），失败返回空
    std::optional<std::string> get_string(const std::string & key, DiagnosticList * diags = nullptr) const;
    std::optional<double> get_double(const std::string & key, DiagnosticList * diags = nullptr) const;
    std::optional<int64_t> get_int64(const std::string & key, DiagnosticList * diags = nullptr) const;
    std::optional<bool> get_bool(const std::string & key, DiagnosticList * diags = nullptr) const;

    // 取数组（容器拷贝），适用于自定义遍历
    std::optional<ConfigArray> get_array(const std::string & key, DiagnosticList * diags = nullptr) const;

    // 直接返回对象
    const ConfigObject & object() const { return _object; }

private:
    std::string _name;
    ConfigObject _object;
    std::vector<std::string> _path; // 仅作为诊断信息记录
};

} // namespace cem